// Dep 收集watcher，等data改变，调用notify
class Dep {
  constructor() {
    this.callbacks = []
  }
  addCb(watcher) {
    this.callbacks.push(watcher)
  }
  notify() {
    this.callbacks.forEach(item => {
      item.update()
    })
  }
}

const dep = new Dep()

function Observer(obj) {
  if (typeof obj !== 'object' || !obj) return;
  Object.keys(obj).forEach(key => {
    let value = obj[key]
    Observer(value)
    Object.defineProperty(obj, key, {
      get() {
        console.log('getter')
        return value
      },
      set(newValue) {
        console.log('setter')
        value = newValue
        Observer(newValue)
        dep.notify()
      }
    })
  })
}


function Compile(el, vm) {
  // 文档碎片， 提升性能
  // 文档碎片 把dom放在内存里，在内存操作dom,最后渲染到页面中
  const node = document.createDocumentFragment()

  Array.from(el.childNodes).forEach(item => {
    node.appendChild(item)
  })


  function replace(dom) {
    Array.from(dom.childNodes).forEach(node => {
      if (node.nodeType === 3) {
        // 替换
        const reg = /\{\{\s*(\S+)\s*\}\}/
        const text = node.textContent;
        console.log('text', text)
        const result = reg.exec(text)
        console.log('result', result)
        if (result) {
          // node.textContent = text.replace(
          //   result[0],
          //   result[1].split('.').reduce((prev, next) => prev[next], vm))
          new Watcher(vm, result[1], (value) => {
            node.textContent = text.replace(
              result[0],
              value)
          })
        }

        return
      } else if (node.nodeType === 1) {
        // console.log([...node.attributes], node, '===========')
        const attribute = [...node.attributes].find(item => item.name === 'v-model')
        if (attribute) {
          new Watcher(vm, attribute.value, (value) => {
            node.value = value
          })
          node.addEventListener('input', (e) => {
            const keys = attribute.value.split('.');
            const newObj = keys.slice(0, keys.length - 1).reduce((prev, next) => prev[next], vm)
            newObj[keys[keys.length - 1]] = e.target.value
          })
        }
      }


      replace(node)
    })
  }
  replace(node)
  el.appendChild(node)
  // 
}




class Vue {
  constructor(options) {
    this.$el = document.querySelector(options.el);
    this.$data = options.data;
    Observer(this.$data)
    Object.keys(this.$data).forEach(key => {
      Object.defineProperty(this, key, {
        get() {
          return this.$data[key]
        },
        set(newValue) {
          this.$data[key] = newValue
        }
      })
    })
    Compile(this.$el, this)
    dep.notify()
  }
}

class Watcher {
  constructor(vm, key, callback) {
    this.cb = callback
    this.key = key;
    dep.addCb(this)
    this.vm = vm
  }
  update() {
    const value = this.key.split('.').reduce((prev, next) => prev[next], this.vm)
    this.cb(value)
  }
}



